import Queue from "promise-queue-plus";
import axios from "axios";
import request from "@/request";
import md5 from "@/lib/md5";
import { ElLoading } from "element-plus";

const fileUploadChunkQueue = {};
let percent = 0;
let loading = null;

const taskInfo = (identifier) => {
  return request({
    url: `/v1/minio/tasks/${identifier}`,
    method: "get",
  });
};

const initTask = ({ identifier, fileName, totalSize, chunkSize }) => {
  return request({
    url: "/v1/minio/tasks",
    method: "post",
    data: { identifier, fileName, totalSize, chunkSize },
  });
};

const preSignUrl = ({ identifier, partNumber }) => {
  return request({
    url: `/v1/minio/tasks/${identifier}/${partNumber}`,
    method: "get",
  });
};

const merge = (identifier) => {
  return request({
    url: `/v1/minio/tasks/merge/${identifier}`,
    method: "post",
  });
};

const getTaskInfo = async (file) => {
  let task;
  const identifier = await md5(file);
  const { code, data, msg } = await taskInfo(identifier);
  if (code === 200) {
    task = data;
    if (!task) {
      const initTaskData = {
        identifier,
        fileName: file.name,
        totalSize: file.size,
        chunkSize: 6 * 1024 * 1024,
      };
      const { code, data, msg } = await initTask(initTaskData);
      if (code === 200) {
        task = data;
      } else {
        ElNotification.error({
          title: "文件上传错误",
          message: msg,
        });
      }
    }
  } else {
    ElNotification.error({
      title: "文件上传错误",
      message: msg,
    });
  }
  return task;
};

const handleUpload = (file, taskRecord) => {
  let lastUploadedSize = 0; // 上次断点续传时上传的总大小
  let uploadedSize = 0; // 已上传的大小
  const totalSize = file.size || 0; // 文件总大小
  let startMs = new Date().getTime(); // 开始上传的时间
  const { exitPartList, chunkSize, chunkNum, fileIdentifier } = taskRecord;

  // 获取从开始上传到现在的平均速度（byte/s）
  const getSpeed = () => {
    // 已上传的总大小 - 上次上传的总大小（断点续传）= 本次上传的总大小（byte）
    const intervalSize = uploadedSize - lastUploadedSize;
    const nowMs = new Date().getTime();
    // 时间间隔（s）
    const intervalTime = (nowMs - startMs) / 1000;
    return intervalSize / intervalTime;
  };

  const uploadNext = async (partNumber) => {
    const start = new Number(chunkSize) * (partNumber - 1);
    const end = start + new Number(chunkSize);
    const blob = file.slice(start, end);
    const { code, data, msg } = await preSignUrl({
      identifier: fileIdentifier,
      partNumber: partNumber,
    });
    if (code === 200 && data) {
      await axios.request({
        url: data,
        method: "PUT",
        data: blob,
        isShardUpload: true,
        headers: { "Content-Type": "application/octet-stream" },
      });
      return Promise.resolve({
        partNumber: partNumber,
        uploadedSize: blob.size,
      });
    }
    return Promise.reject(`分片${partNumber}， 获取上传地址失败`);
  };

  /**
   * 更新上传进度
   * @param increment 为已上传的进度增加的字节量
   */
  const updateProcess = (increment) => {
    increment = new Number(increment);
    let factor = 1000; // 每次增加1000 byte
    let from = 0;
    // 通过循环一点一点的增加进度
    while (from <= increment) {
      from += factor;
      uploadedSize += factor;
      percent = Math.round((uploadedSize / totalSize) * 100).toFixed(2);
    }

    const speed = getSpeed();
    const remainingTime =
      speed != 0 ? Math.ceil((totalSize - uploadedSize) / speed) + "s" : "未知";
    console.log("剩余大小：", (totalSize - uploadedSize) / 1024 / 1024, "mb");
    console.log("当前速度：", (speed / 1024 / 1024).toFixed(2), "mbps");
    console.log("预计完成：", remainingTime);
    loading?.setText(`上传中，${percent > 100 ? 100 : percent}%`);
  };

  return new Promise((resolve) => {
    const failArr = [];
    const queue = Queue(5, {
      retry: 3, //Number of retries
      retryIsJump: false, //retry now?
      workReject: function (reason, queue) {
        failArr.push(reason);
      },
      queueEnd: function (queue) {
        resolve(failArr);
      },
    });
    fileUploadChunkQueue[file.uid] = queue;
    for (let partNumber = 1; partNumber <= chunkNum; partNumber++) {
      const exitPart = (exitPartList || []).find(
        (exitPart) => exitPart.partNumber == partNumber
      );
      if (exitPart) {
        // 分片已上传完成，累计到上传完成的总额中,同时记录一下上次断点上传的大小，用于计算上传速度
        lastUploadedSize += new Number(exitPart.size);
        updateProcess(exitPart.size);
      } else {
        queue.push(() =>
          uploadNext(partNumber).then((res) => {
            // 单片文件上传完成再更新上传进度
            updateProcess(res.uploadedSize);
          })
        );
      }
    }
    if (queue.getLength() == 0) {
      // 所有分片都上传完，但未合并，直接return出去，进行合并操作
      resolve(failArr);
      return;
    }
    queue.start();
  });
};

export const shardUpload = async (file) => {
  const task = await getTaskInfo(file);
  if (task) {
    const { finished, path, originalFileName, taskRecord } = task;
    const { fileIdentifier: identifier } = taskRecord;
    if (finished) {
      return {
        fileSize: file.size,
        originalFilename: originalFileName,
        url: path,
        name: originalFileName,
      };
    } else {
      loading = ElLoading.service({
        lock: true,
        text: `上传中，${percent}%`,
        background: "rgba(0, 0, 0, 0.7)",
      });
      console.log(loading);
      const errorList = await handleUpload(file, taskRecord);
      loading.close();
      if (errorList.length > 0) {
        ElNotification.error({
          title: "文件上传错误",
          message: "部分分片上次失败，请尝试重新上传文件",
        });
        return;
      }
      const { code, msg } = await merge(identifier);
      if (code === 200) {
        return {
          fileSize: file.size,
          originalFilename: originalFileName,
          url: path,
          name: originalFileName,
        };
      } else {
        ElNotification.error({
          title: "文件上传错误",
          message: msg,
        });
      }
    }
  } else {
    ElNotification.error({
      title: "文件上传错误",
      message: "获取上传任务失败",
    });
  }
};
